/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package lineage.core;

import java.io.IOException;
import java.net.Socket;

import lineage.controller.L1AccountController;
import lineage.controller.pc.L1PcController;
import lineage.core.encryptions.Encryption;
import lineage.core.handler.L1ReadHandler;
import lineage.core.handler.L1WriteHandler;
import lineage.database.tables.bean.L1Account;
import lineage.model.instance.L1PcInstance;
import lineage.packet.L1AbstractPacketServer;
import lineage.util.StreamUtil;
import lineage.util.Util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 处理客户端相关.
 * 
 * @version 2014年3月8日上午12:52:28
 * @author jrwz
 */
public final class L1GameClient implements Runnable {
    private static final Logger LOG = LoggerFactory.getLogger(L1GameClient.class);
    /** 客户端套接字. */
    private Socket socket;
    /** 客户端IP地址. */
    private final StringBuilder ip;
    /** 密钥. */
    private Encryption keys;
    /** 读处理器. */
    private L1ReadHandler read;
    /** 写处理器. */
    private L1WriteHandler write;
    /** 是否开始处理. */
    private boolean isRun = true;
    /** 账号名称. */
    private String accountName;
    /** 账号. */
    private L1Account account;
    /** 在线角色. */
    private L1PcInstance onlineChar;

    /**
     * 客户端.
     * 
     * @param socket
     *            - 套接字
     * @throws IOException
     */
    public L1GameClient(final Socket socket) throws IOException {
        this.socket = socket;
        ip = new StringBuilder().append(socket.getInetAddress().getHostAddress());
        keys = new Encryption();
        read = new L1ReadHandler(this, socket.getInputStream());
        write = new L1WriteHandler(this, socket.getOutputStream());
    }

    @Override
    public void run() {
        try {
            write.outFirstPacket(); // 送出第一组封包

            final StringBuilder sb = new StringBuilder();
            sb.append("连线信息：\t(");
            sb.append(ip.toString());
            sb.append(")\t连接到服务器 ");
            Util.message(Util.SYSTEM_MESSAGE, sb.toString());

            while (isRun) {
                byte[] data = null;
                try {
                    data = read.toDecrypt();
                } catch (final Exception e) {
                    // TODO
                    break;
                }
                read.execute(data); // 处理客户端封包
            }
        } catch (final Exception e) {
            // TODO
        } finally {
            close();
        }
        return;
    }

    /**
     * 关闭.
     */
    private void close() {
        if (socket == null) {
            return;
        }

        read.close();
        write.close();
        StreamUtil.close(socket);

        read = null;
        write = null;
        socket = null;
        keys = null;
        isRun = false;

        quitGame(); // 离线处理

        final StringBuilder sb = new StringBuilder();
        sb.append("断线信息：\t(");
        sb.append(ip.toString());
        sb.append(")\t断开连接 ");
        Util.message(Util.SYSTEM_MESSAGE, sb.toString());
    }

    /**
     * 取得客户端套接字.
     * 
     * @return 对应客户端的Socket
     */
    public Socket getSocket() {
        return socket;
    }

    /**
     * 取得IP地址.
     * 
     * @return 对应客户端的IP地址
     */
    public StringBuilder getIp() {
        return ip;
    }

    /**
     * 取得密钥.
     * 
     * @return 密钥
     */
    public Encryption getKeys() {
        return keys;
    }

    /**
     * 取得读处理器.
     * 
     * @return 读处理器类实体
     */
    public L1ReadHandler getRead() {
        return read;
    }

    /**
     * 取得写处理器.
     * 
     * @return 写处理器类实体
     */
    public L1WriteHandler getWrite() {
        return write;
    }

    /**
     * 向客户端发送封包.
     * 
     * @param data
     *            - 要发送的数据
     */
    public void sendPacket(final byte[] data) {
        write.executeQueue(data);
    }

    /**
     * 向客户端发送封包.
     * 
     * @param packet
     *            - 要发送的服务端封包
     */
    public void sendPacket(final L1AbstractPacketServer packet) {
        write.executeQueue(packet);
    }

    /**
     * 取得账号.
     * 
     * @return 账号(L1Account对象)
     */
    public L1Account getAccount() {
        return account;
    }

    /**
     * 设置账号.
     * 
     * @param account
     *            - 要设置的账号(L1Account对象)
     */
    public void setAccount(L1Account account) {
        this.account = account;
    }

    /**
     * 取得账号名称.
     * 
     * @return 账号名称
     */
    public String getAccountName() {
        return accountName;
    }

    /**
     * 设置账号名称.
     * 
     * @param name
     *            - 要设置的账号名称
     */
    public void setAccountName(String name) {
        accountName = name;
    }

    /**
     * 取得在线角色.
     * 
     * @return 在线角色
     */
    public L1PcInstance getOnlineChar() {
        return onlineChar;
    }

    /**
     * 设置在线角色.
     * 
     * @param onlineChar
     *            - 要设置的在线角色
     */
    public void setOnlineChar(L1PcInstance onlineChar) {
        if (this.onlineChar != null) {
            return;
        }
        this.onlineChar = onlineChar;
    }

    /**
     * 角色离线处理.
     */
    public void quitChar() {
        if (onlineChar == null) {
            return;
        }

        synchronized (onlineChar) {
            L1PcController.QuitGame(onlineChar);
            onlineChar = null;
        }
    }

    /**
     * 退出游戏相关处理.
     */
    public void quitGame() {
        L1AccountController.getInstance().remove(accountName); // 从'帐号在线列表'内删除该帐号
        quitChar(); // 角色离线处理
    }
}
